<!--
 * @Author: kls
 * @Date: 2024-07-02 10:46:35
 * @LastEditTime: 2024-07-10 18:29:24
 * @LastEditors: liz
 * @Description: threeJs 地图
 * @FilePath: \fankongwuzhi_front\src\views\chart\threemap.vue
-->
<template>
  <div class="map-gd" @mousemove="debouncedHandleMouseMove">
    <div id="province"></div>
    <template v-if="isShowBackTo">
      <div
        v-if="mapCurrentLevel != 'province'"
        class="back-btn"
        @click="backToPrevious"
      ></div>
    </template>

    <!--  -->
    <div
      v-if="showInfoPopupsStatus && Object.keys(popupsInfo).length !== 0"
      class="info-popups"
      id="infoPopups"
      style="left: 890px; top: 320px"
    >
      <div class="popups-li">
        <span>
          {{ popupsInfo.name }}
        </span>
      </div>
      <div class="popups-li">
        <span>单位数量：</span>
        <span> {{ popupsInfo.data }}</span>
        <span style="color: #cfe3fc; font-size: 12px">&nbsp;家</span>
      </div>
      <div class="popups-li">
        <span>绿色单位：</span>
        <span class="span-green"> {{ popupsInfo.data1 }}</span> <span style="color: #cfe3fc; font-size: 12px">&nbsp;家</span>
      </div>
      <div class="popups-li">
        <span>橙色单位：</span>
        <span class="span-orange"> {{ popupsInfo.data2 }} </span> <span style="color: #cfe3fc; font-size: 12px">&nbsp;家</span>
      </div>
      <div class="popups-li">
        <span>红色单位：</span>
        <span class="span-red"> {{ popupsInfo.data3 }}</span> <span style="color: #cfe3fc; font-size: 12px">&nbsp;家</span>
      </div>
    </div>
    <div
      v-if="
        showUnitInfoPopupsStatus && Object.keys(unitPopupsInfo).length !== 0
      "
      class="info-popups street-info-popups"
      id="unitInfoPopups"
      style="left: 890px; top: 320px"
    >
      <div class="popups-li">
        <span class="li-label">单位名称：</span>
        <span class="li-info">
          {{ unitPopupsInfo.name }}
        </span>
      </div>
      <div class="popups-li">
        <span class="li-label">地址：</span>
        <span class="li-info">
          {{ unitPopupsInfo.streetAddress }}
        </span>
      </div>
      <div class="popups-li">
        <span class="li-label">单位类型：</span>
        <span class="li-info"> {{ unitPopupsInfo.streetType }} </span>
      </div>
      <div class="popups-li">
        <span class="li-label">单位级别：</span>
        <span class="li-info"> {{ unitPopupsInfo.streetLevel }} </span>
      </div>
      <div class="popups-li">
        <span class="li-label">行业主管：</span>
        <span class="li-info"> {{ unitPopupsInfo.streetIndustry }} </span>
      </div>
      <div class="popups-li">
        <span class="li-label">最近检查时间：</span>
        <span class="li-info"> {{ unitPopupsInfo.streetIndustry }} </span>
      </div>
    </div>
    <div class="full-bottom">
      <vue3-seamless-scroll
        :list="warnDtlListdata"
        hover
        wheel
        :singleHeight="30"
        :singleWaitTime="2000"
        :limitScrollNum="2"
        class="scroll"
      >
        <div
          class="bottom-item"
          v-for="(item, index) in warnDtlListdata"
          :key="index"
        >
          <div class="title">{{ item.typeName }}</div>
          <div class="content" style="width: 570px">
            {{ item.warnName }}
          </div>
          <div class="time">{{ item.createTime }}</div>
          <img
            src="@/assets/images/jumpback.png"
            alt=""
            style="width: 16px; height: 16px"
            @click="handlerdanger(item)"
          />
        </div>
      </vue3-seamless-scroll>
    </div>
    <div
      class="full-top"
      :class="{ 'full-top-vertical': mapCurrentLevel == 'district' }"
    >
      <div class="top-item special-item" @click="handlerjump">
        <div class="item-title">重点单位</div>
        <div class="item-num">
          <span class="number">{{ data.cnt1 || 0 }}</span>
          <span class="unit">家</span>
        </div>
      </div>
      <div class="top-item">
        <div class="item-title">累计检查</div>
        <div class="item-num">
          <span class="number">{{ data.checkCnt || 0 }}</span>
          <span class="unit">条</span>
        </div>
      </div>
      <div class="top-item">
        <div class="item-title">隐患上报</div>
        <div class="item-num">
          <span class="number">{{ data.warnCnt || 0 }}</span>
          <span class="unit">条</span>
        </div>
      </div>
      <div class="top-item">
        <div class="item-title">待处置</div>
        <div class="item-num">
          <span class="number">{{ data.warnNoCheckCnt || 0 }}</span>
          <span class="unit">条</span>
        </div>
      </div>
    </div>
    <div v-if="driveScoredata.length > 0" class="full-right">
      <div
        class="right-item"
        v-for="(item, index) in driveScoredata"
        :key="index"
      >
        <div class="city">{{ item.name }}</div>
        <div class="num">{{ item.data || 0 }}个</div>
        <div class="proportion">{{ item.profit }}%</div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { onMounted, onBeforeUnmount, ref } from "vue";
import * as THREE from "three";
import { geoMercator } from "d3-geo";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { mergeGeometries } from "three/examples/jsm/utils/BufferGeometryUtils";
import { InteractionManager } from "three.interactive";
import ResourceTracker from "../../utils/TrackResource.js";
import useUserStore from "@/store/modules/user";
import {
  CSS2DObject,
  CSS2DRenderer,
} from "three/examples/jsm/renderers/CSS2DRenderer.js";
import {
  getuserPostion,
  getdriveScoreGetZone,
  getdriveScoreGetStreet,
  getdriveScore,
  getareacode,
} from "@/api/chart/chart";
import { Vue3SeamlessScroll } from "vue3-seamless-scroll";
import { center as turfCenter } from "@turf/turf";
import sidePng from "@/assets/texture/side.png";
import guangquan01 from "@/assets/texture/guangquan01.png";
import guangquan02 from "@/assets/texture/guangquan02.png";
import { useRouter } from "vue-router";

const router = useRouter();
// 在外层定义resMgr和track
let resMgr = new ResourceTracker();
const track = resMgr.track.bind(resMgr);
const userStore = useUserStore();

const props = defineProps({
  // 操作类型
  searchProvincesMunicipalities: {
    type: Array,
    default: [],
  },
});
let renderer = null;
let camera = null;
let scene = null;
let css2DRenderer = null;
let controller = null;
let interactionManager = null;
let currentMouseClient = { x: 0, y: 0 };
/**
 * @description: 鼠标事件
 * @param {*} debounce
 */
const debouncedHandleMouseMove = (event) => {
  currentMouseClient = {
    x: event.clientX,
    y: event.clientY,
  };
};
/**
 * @description: 获取用户位置
 */
let mapEchartcode = ref("");
const getuserPostiondata = () => {
  getuserPostion({
    type: 1,
  }).then((res) => {
    let { userPostion } = res;
    if (userStore.authorityType == 0) {
      mapEchartcode.value = userPostion.provinceCode;
    } else if (userStore.authorityType == 1) {
      mapEchartcode.value = userPostion.cityCode;
    } else if (userStore.authorityType == 2) {
      mapEchartcode.value = userPostion.zoneCode;
    } else {
      mapEchartcode.value = userPostion.zoneCode;
    }
  });
};
onMounted(() => {
  initRenderer();
  initCamera();
  initScene();
  initLight();
  initControls();
  animate();
  //   axesHelper();
  //   loadMapJson();
  getuserPostiondata();
  window.addEventListener("resize", onWindowResize(), false);
  //创建地图底部网格
  createGrid();
});
onBeforeUnmount(() => {
  window.removeEventListener("resize", onWindowResize(), false);
  interactionManager.dispose();
  try {
    scene.clear();
    resMgr && resMgr.dispose();
    renderer.dispose();
    renderer.forceContextLoss();
    renderer.content = null;
    cancelAnimationFrame(() => {
      renders();
      animate();
    });
    let gl = renderer.domElement.getContext("webgl");
    gl && gl.getExtension("WEBGL_lose_context").loseContext();
    renderer.domElement = null;
    console.log(1, renderer.info); //查看memery字段即可
  } catch (e) {
    console.log(e);
  }
});
let data = ref({});
let driveScoredata = ref([]);
let icondata = ref([]);
let warnDtlListdata = ref([]);
let threedPlate = track(new THREE.Object3D());
let threedPoint = track(new THREE.Object3D());
let threedPixelsPoint = track(new THREE.Object3D());
const plateThickness = 1; // 板块厚度
let eventElement = [];
let defaultMaterial = null;
let defaultLightMaterial = null;
let mapCurrentLevel = ref("province");
let isShowBackTo = ref(true);
let mapCurrentProperties = ref({});
let mapCheckCode = ref("");
let pointCenter = [120.109913, 29.181466];
let d3Scale = 120;
// 声明你想要触发的事件名称
const emit = defineEmits(["back-to", "reverse-lookup"]);
/**
 * @description: 处理跳转
 */
const handlerjump = () => {
  router.push({
    path: "/erp",
  });
};
/**
 * @description: 处理跳转
 */
const handlerdanger = (item) => {
  // router.push({
  //   path: "/danger",
  //   query: {
  //     indexStrip: true,
  //     businessId: item.taskId,
  //     taskDisposalId: item.taskDisposalId,
  //     buttonAuthorityList: item.buttonAuthorityList,
  //     followStatus: item.followStatus,
  //     name: item.name,
  //     nodeTaskDisposalId: item.nodeTaskDisposalId,
  //     industryId: item.industryId,
  //   },
  // });
};
/**
 * @description: 接受传递信息
 * @param {*} info
 * @param {*} driveScore
 * @param {*} warnDtlList
 * @param {*} params
 */
const accept = async (info, driveScore = [], warnDtlList, params) => {
  data.value = info;
  driveScoredata.value = driveScore;
  warnDtlListdata.value = warnDtlList;
  if (params.region.length > 0) {
    let adcode = "";
    switch (userStore.authorityType) {
      case 0:
        if (params.region.length == 1) {
          await getareacode(params.cityId).then((res) => {
            adcode = res.data.code;
            renderMapEcharts(`${res.data.code}`, "city", params); // 初始化地图
          });
        }
        if (params.region.length == 2) {
          await getareacode(params.zoneId).then((res) => {
            adcode = res.data.code;
            renderMapEcharts(`${res.data.code}`, "district", params); // 初始化地图
          });
        }
        if (params.region.length == 3) {
          await getareacode(params.zoneId).then((res) => {
            adcode = res.data.code;
            renderMapEcharts(`${res.data.code}`, "street", params); // 初始化地图
          });
        }
        break;
      case 1:
        if (params.region.length == 1 || params.region.length == 2) {
          await getareacode(params.zoneId).then((res) => {
            let { currentLevel } = administrativeDivisionCodeProcessing(
              res.data.code.toString()
            );
            renderMapEcharts(`${res.data.code}`, currentLevel, params); // 初始化地图
          });
        }
        break;
      default:
        break;
    }
  } else {
    let { currentLevel } = administrativeDivisionCodeProcessing(
      mapEchartcode.value.toString()
    );
    let mapLevel = "";
    if (userStore.authorityType == 0 || userStore.authorityType == 1) {
      mapLevel = currentLevel;
    }
    if (userStore.authorityType == 2) {
      mapLevel = currentLevel;
    }
    if (userStore.authorityType == 3) {
      mapLevel = "street";
    }
    if (userStore.authorityType == 2 || userStore.authorityType == 3) {
      renderMapEcharts(`${mapEchartcode.value}`, mapLevel, params); // 初始化地图
    } else {
      renderMapEcharts(`${mapEchartcode.value}`, mapLevel, params); // 初始化地图
    }
  }
};
/**
 * @description: 切换地图板块
 * @param {*} mapName
 * @param {*} level
 */
const renderMapEcharts = async (mapName, level = "", params) => {
  let url = "";
  let levelList = {
    province: 3,
    city: 2,
    district: 1,
    street: 0,
  };
  let mapLevel = "";
  if (level == "") {
    let { currentLevel } = administrativeDivisionCodeProcessing(mapName);
    mapLevel = currentLevel;
  } else {
    mapLevel = level;
  }
  //调回默认视角
  camera.position.set(position.x, position.y, position.z);
  switch (mapLevel) {
    case "street":
      url = mapName;
      d3Scale = 800;
      break;
    case "district":
      url = mapName;
      d3Scale = 800;
      break;
    case "city":
      url = mapName + "_full";
      d3Scale = 300;
      break;
    case "province":
      url = mapName + "_full";
      d3Scale = 120;
      break;
    default:
      break;
  }
  if (mapLevel) {
    if (threedPlate.children.length > 0) {
      eventElement.map((mesh) => {
        interactionManager.remove(mesh);
      });
      eventElement = [];
      disposeAndRemoveChildren(threedPlate);
      disposeAndRemoveChildren(threedPoint);
      disposeAndRemoveChildren(threedPixelsPoint);
    }
    popupsInfo.value = {};
    unitPopupsInfo.value = {};
    showUnitInfoPopupsStatus.value = false;
    showInfoPopupsStatus.value = false;
    ringList = [];
    mapCheckCode.value = mapName;
    mapCurrentLevel.value = mapLevel;
    const mapJson = await getMapJson(url);
    let center = turfCenter(mapJson);
    pointCenter = center.geometry.coordinates;
    createSection(mapJson);
    handleClick(mapName, mapLevel, params);

    let currentPermissions = levelList[mapLevel];
    let authorityType = userStore.authorityType;
    if (userStore.authorityType == 3) {
      authorityType = 2;
    }
    if (3 - authorityType > currentPermissions) {
      isShowBackTo.value = true;
    }
    if (3 - authorityType == currentPermissions) {
      isShowBackTo.value = false;
    }
  }
};
/**
 * @description: 获取json数据
 * @param {*} mapName
 */
const getMapJson = async (mapName) => {
  const url = `http://geo.datav.aliyun.com/areas_v3/bound/${mapName}.json`;
  const mapJson = await fetch(url).then((res) => res.json());
  return mapJson;
};
const handleClick = async (adcode, level, params) => {
  let func;
  let obj = {};
  switch (level) {
    case "street":
      obj = {
        ...params,
      };
      func = getdriveScore;
      break;
    case "district":
      obj = {
        areaCode: adcode,
        ...params,
      };
      func = getdriveScoreGetStreet;
      break;
    case "city":
      obj = {
        areaCode: adcode,
        ...params,
      };
      func = getdriveScoreGetZone;
      break;
    case "province":
      //   obj = {
      //     type: 1,
      //   };
      obj = {
        ...params,
      };
      func = getdriveScore;
      break;
    default:
      break;
  }
  driveScoredata.value = [];
  icondata.value = [];
  let res = await func(obj);
  if (level == "street") {
    driveScoredata.value = [];
    let arr = res.driveScore.filter((item) => item.data != 0);
    icondata.value = arr;
    createPixelsPoint(icondata.value);
  } else if (level == "district") {
    driveScoredata.value = [];
    icondata.value = res.driveScoreStreet;
    createPixelsPoint(icondata.value);
  } else if (level == "city") {
    icondata.value = [];
    driveScoredata.value = res.driveScoreZone;
    createPoint(res.driveScoreZone);
  } else if (level == "province") {
    icondata.value = [];
    driveScoredata.value = res.driveScore;
    createPoint(res.driveScore);
  }
};
/**
 * @description: 返回上一级
 */
const backToPrevious = () => {
  if (props.searchProvincesMunicipalities.length > 0) {
    emit("back-to");
  } else {
    let { parentAdcode, parentLevel, currentLevel } =
      administrativeDivisionCodeProcessing(mapCheckCode.value.toString());
    renderMapEcharts(parentAdcode, parentLevel);
  }
};
const reverseLookup = (adcode) => {
  emit("reverse-lookup", adcode);
};
/**
 * @description: 处理行政区划代码
 * @param {*} code
 */
const administrativeDivisionCodeProcessing = (code) => {
  let levelComparison = {
    1: "province",
    2: "city",
    3: "district",
  };
  let result = [];
  for (let i = 0; i < code.length; i += 2) {
    let temp = code.substring(i, i + 2);
    if (temp != "00") {
      result.push(temp);
    }
  }
  let currentLevel = levelComparison[result.length];
  let parentLevel = levelComparison[result.length - 1] || "province";
  let parentAdcode = "";
  switch (parentLevel) {
    case "city":
      parentAdcode = result[0] + result[1] + "00";
      break;
    case "province":
      parentAdcode = result[0] + "0000";
      break;
    default:
      break;
  }
  return { parentAdcode, parentLevel, currentLevel };
};

const showInfoPopupsStatus = ref(false);
const showUnitInfoPopupsStatus = ref(false);
const popupsInfo = ref({});
const unitPopupsInfo = ref({});
/**
 * @description: 信息弹窗显示
 * @param {*} plateInfo
 */
const showInfoPopps = (plateInfo) => {
  setTimeout(() => {
    if (showInfoPopupsStatus.value) {
      if (driveScoredata.value.length > 0) {
        driveScoredata.value.forEach((item) => {
          if (item.adcode == plateInfo.adcode) {
            popupsInfo.value = item;
          }
        });
        const dom = document.querySelector("#infoPopups");
        if (currentMouseClient.y > 500) {
          currentMouseClient.y += -200;
        }
        if (currentMouseClient.x > 900) {
          currentMouseClient.x += -200;
        }
        if (dom?.style) {
          dom.style.top = currentMouseClient.y + 20 + "px";
          dom.style.left = currentMouseClient.x + 20 + "px";
        }
      }
    }
  });
};
const showUnitInfoPopps = (plateInfo) => {
  setTimeout(() => {
    if (showUnitInfoPopupsStatus.value) {
      if (icondata.value.length > 0) {
        unitPopupsInfo.value = plateInfo;
        const dom = document.querySelector("#unitInfoPopups");
        if (currentMouseClient.y > 350) {
          currentMouseClient.y += -200;
        }
        if (currentMouseClient.x > 900) {
          currentMouseClient.x += -200;
        }
        if (dom?.style) {
          dom.style.top = currentMouseClient.y + 20 + "px";
          dom.style.left = currentMouseClient.x + 20 + "px";
        }
      }
    }
  });
};
/**
 * 初始化渲染器，创建THREE.js的WebGLRenderer对象并设置相关属性
 */
const initRenderer = () => {
  /**
   * 创建一个新的WebGL渲染器实例，启用抗锯齿和透明背景
   * @type {THREE.WebGLRenderer}
   */
  renderer = new THREE.WebGLRenderer({
    antialias: true, // 启用抗锯齿
    alpha: true, // 允许透明背景
    // logarithmicDepthBuffer: true, // 启用对数深度缓冲区
  });

  // 设置渲染器的像素比，提高在高分辨率屏幕上的显示质量
  renderer.setPixelRatio(window.devicePixelRatio);

  // 设置渲染器的大小与浏览器窗口大小一致
  renderer.setSize(window.innerWidth, window.innerHeight);

  // 注释掉的代码：原本可能用于设置输出编码为THREE.sRGBEncoding
  // this.renderer.outputEncoding = THREE.sRGBEncoding;

  // 设置渲染器的背景颜色为白色，并设置透明度为0以实现透明背景
  renderer.setClearColor(0xffffff, 0);

  // 获取DOM元素，用于放置渲染器的输出和性能统计
  const containerDom = document.querySelector("#province");

  // 将渲染器的canvas元素添加到指定的DOM容器
  containerDom.appendChild(renderer.domElement);
};
/**
 * 初始化相机，创建一个透视相机并设置其属性
 * @description 创建一个THREE.js的透视相机，并调整其位置以面向坐标原点
 */
let position = {
  x: 2.3216593497968696,
  y: -12.592285483500039,
  z: 19.0884844166664,
};
const initCamera = () => {
  /**
   * 创建一个具有45度视野角的透视相机，根据窗口宽高比设置纵横比，
   * 并设置近裁剪面为0.1，远裁剪面为5000
   */
  camera = new THREE.PerspectiveCamera(
    45,
    window.innerWidth / window.innerHeight,
    1,
    5000
  );

  camera.position.set(position.x, position.y, position.z);
  //   camera.position.set(0, -13, 7);
  camera.lookAt(0, 0, 0);
  window.camera = camera;
};
/**
 * 创建一个新的THREE.Scene实例，用于存储场景中的所有对象。
 * 并设置场景的背景颜色为指定的十六进制颜色值。
 */
const initScene = () => {
  scene = new THREE.Scene(); // 创建场景
  scene.background = new THREE.Color(0x102736); // 设置场景背景色
};
/**
 * @description 创建一个OrbitControls实例，并设置其属性以限制用户交互
 * @see {@link https://threejs.org/docs/#examples/en/controls/OrbitControls OrbitControls文档}
 */
const initControls = () => {
  interactionManager = new InteractionManager(
    renderer,
    camera,
    renderer.domElement
  );
  controller = new OrbitControls(camera, renderer.domElement);
  let target = {
    x: 4.006309549712105,
    y: -0.8547228306382056,
    z: 0.1347813685356284,
  };
  controller.target.set(target.x, target.y, target.z);
  controller.update();
  // 注意：以下属性默认启用，这里已注释掉以禁止相应操作
  //   controller.enablePan = false; // 禁用摄像机平移
  //   controller.enableZoom = false; // 禁用摄像机缩放
  controller.enableRotate = false; // 禁用摄像机旋转
  //   controller.maxDistance = 50; // 设置最大缩放距离（仅适用于PerspectiveCamera）
  //   controller.minDistance = 10; // 设置最小缩放距离
  window.controller = controller;
  css2DRenderer = new CSS2DRenderer();
  css2DRenderer.setSize(window.innerWidth, window.innerHeight);
  css2DRenderer.render(scene, camera);
  css2DRenderer.domElement.style.position = "absolute";
  css2DRenderer.domElement.style.top = 0;
  css2DRenderer.domElement.style.pointerEvents = "none"; // 取消标签的点击事件};
  window.document
    .getElementById("province")
    .appendChild(css2DRenderer.domElement);
};
/**
 * 初始化光照设置，包括点光源和聚光灯
 */
const initLight = () => {
  const ambientLight = new THREE.AmbientLight("rgba(255,255,255,1)", 1); // 环境光
  scene.add(ambientLight);
  let directionalLight = new THREE.DirectionalLight(0xffffff, 5);
  directionalLight.position.set(-30, 6, -8);
  directionalLight.castShadow = true;
  directionalLight.shadow.radius = 20;
  directionalLight.shadow.mapSize.width = 1024;
  directionalLight.shadow.mapSize.height = 1024;
  scene.add(directionalLight);
  createPointLight({
    color: "#1d5e5e",
    // intensity: 27,
    intensity: 600,
    distance: 10000,
    x: -9,
    y: 3,
    z: -3,
  });
  createPointLight({
    color: "#1d5e5e",
    intensity: 100,
    distance: 10000,
    x: 0,
    y: 2,
    z: 5,
  });
  //   const pointLight = track(new THREE.PointLight(0xffffff, 1));
  //   pointLight.position.set(20, 50, 150);
  //   pointLight.castShadow = true;
  //   pointLight.shadow.mapSize.width = 1024;
  //   pointLight.shadow.mapSize.height = 1024;

  //   const pointLight2 = track(new THREE.SpotLight(0xffffff, 0.7));
  //   pointLight2.position.set(20, 50, 100);
  //   pointLight2.shadow.mapSize.width = 1024;
  //   pointLight2.shadow.mapSize.height = 1024;

  //   scene.add(pointLight);
  //   scene.add(pointLight2);
};
/**
 * @description: 创建点光源
 * @param {*} pointParams
 */
const createPointLight = (pointParams) => {
  // 点光源
  const pointLight = new THREE.PointLight(
    0x1d5e5e,
    pointParams.intensity,
    pointParams.distance
  );
  pointLight.position.set(pointParams.x, pointParams.y, pointParams.z);
  scene.add(pointLight);
};
/**
 * 当窗口大小改变时，调整相机视角和渲染器的大小。
 * 这确保了画面在不同屏幕尺寸下都能保持正确的宽高比。
 */
const onWindowResize = () => {
  console.log(
    "window.innerWidth, window.innerHeight",
    window.innerWidth,
    window.innerHeight
  );
  camera.aspect = window.innerWidth / window.innerHeight; // 调整相机的宽高比
  camera.updateProjectionMatrix(); // 更新相机的投影矩阵以应用新的宽高比
  renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器的大小为窗口的大小
  renders(); // 执行渲染函数以更新画面
};

/**
 * 执行渲染流程的函数。
 * 它首先清除画布，然后更新统计信息，最后渲染场景。
 */
const renders = () => {
  renderer.clear(); // 清除画布以准备新的渲染

  if (eventElement.length > 0) {
    interactionManager & interactionManager.update();
  }
  renderer.render(scene, camera); // 使用相机渲染场景
};
/**
 * 动画循环函数。
 * 它利用requestAnimationFrame来周期性地调用renders函数和自身，实现动画效果。
 * 这种方式能保证动画的流畅性和性能优化，因为requestAnimationFrame会在浏览器下一次重绘之前调用指定的函数。
 */
const animate = () => {
  window.requestAnimationFrame(() => {
    renders(); // 执行渲染函数
    animate(); // 递归调用自身以维持动画循环
    css2DRenderer.render(scene, camera);
    sideMap.offset.y += 0.005;
    if (ringList.length > 0) {
      ringList.forEach((item) => {
        item.rotation.z += 0.05;
      });
    }
    if (gradPointShader) {
      gradPointShader.uniforms.uTime.value += deltaTime.getDelta();
      if (gradPointShader.uniforms.uTime.value > gradPointMaxTime) {
        gradPointShader.uniforms.uTime.value = 0;
      }
    }
  });
};
let sideMap = new THREE.TextureLoader().load(sidePng);
let guangquan1 = new THREE.TextureLoader().load(guangquan01);
let guangquan2 = new THREE.TextureLoader().load(guangquan02);
let ringList = [];
/**
 * @description: 创建3D板块 材质
 */
const createProvinceMaterial = () => {
  let topMaterial = new THREE.MeshLambertMaterial({
    color: 0xffffff,
    transparent: true,
    opacity: 0,
    fog: false,
    side: THREE.DoubleSide,
  });
  topMaterial.onBeforeCompile = (shader) => {
    shader.uniforms = {
      ...shader.uniforms,
      uColor1: { value: new THREE.Color(0x2a6e92) }, // 419daa
      uColor2: { value: new THREE.Color(0x102736) },
    };
    shader.vertexShader = shader.vertexShader.replace(
      "void main() {",
      `
        attribute float alpha;
        varying vec3 vPosition;
        varying float vAlpha;
        void main() {
          vAlpha = alpha;
          vPosition = position;
      `
    );
    shader.fragmentShader = shader.fragmentShader.replace(
      "void main() {",
      `
        varying vec3 vPosition;
        varying float vAlpha;
        uniform vec3 uColor1;
        uniform vec3 uColor2;

        void main() {
      `
    );
    shader.fragmentShader = shader.fragmentShader.replace(
      "#include <opaque_fragment>",
      /* glsl */ `
      #ifdef OPAQUE
      diffuseColor.a = 1.0;
      #endif

      // https://github.com/mrdoob/three.js/pull/22425
      #ifdef USE_TRANSMISSION
      diffuseColor.a *= transmissionAlpha + 0.1;
      #endif
      vec3 gradient = mix(uColor1, uColor2, vPosition.x/15.78); // 15.78

      outgoingLight = outgoingLight*gradient;

      if(vPosition.z>0.3){
        diffuseColor.a *= 0.5;
      }

      gl_FragColor = vec4( outgoingLight, diffuseColor.a  );
      `
    );
  };
  sideMap.wrapS = THREE.RepeatWrapping;
  sideMap.wrapT = THREE.RepeatWrapping;
  sideMap.repeat.set(1, 1.5);
  sideMap.offset.y += 0.065;
  let sideMaterial = new THREE.MeshStandardMaterial({
    color: 0xffffff,
    map: sideMap,
    fog: false,
    side: THREE.DoubleSide,
  });

  sideMaterial.onBeforeCompile = (shader) => {
    shader.uniforms = {
      ...shader.uniforms,
      uColor1: { value: new THREE.Color(0x2a6e92) },
      uColor2: { value: new THREE.Color(0x2a6e92) },
    };
    shader.vertexShader = shader.vertexShader.replace(
      "void main() {",
      `
        attribute float alpha;
        varying vec3 vPosition;
        varying float vAlpha;
        void main() {
          vAlpha = alpha;
          vPosition = position;
      `
    );
    shader.fragmentShader = shader.fragmentShader.replace(
      "void main() {",
      `
        varying vec3 vPosition;
        varying float vAlpha;
        uniform vec3 uColor1;
        uniform vec3 uColor2;

        void main() {
      `
    );
    shader.fragmentShader = shader.fragmentShader.replace(
      "#include <opaque_fragment>",
      /* glsl */ `
      #ifdef OPAQUE
      diffuseColor.a = 1.0;
      #endif

      // https://github.com/mrdoob/three.js/pull/22425
      #ifdef USE_TRANSMISSION
      diffuseColor.a *= transmissionAlpha + 0.1;
      #endif
      vec3 gradient = mix(uColor1, uColor2, vPosition.z/1.2);

      outgoingLight = outgoingLight*gradient;


      gl_FragColor = vec4( outgoingLight, diffuseColor.a  );
      `
    );
  };
  return [topMaterial, sideMaterial];
};
/**
 * @description: 创建3D板块
 */
const createSection = (JSON) => {
  // 以北京为中心 修改坐标
  const projection1 = geoMercator()
    .center(pointCenter)
    .scale(d3Scale)
    .translate([0, 0]);
  let objectsHover = [];

  const features = JSON.features;
  let LineMaterial = new THREE.LineBasicMaterial({
    color: "#ffffff",
    opacity: 1,
    transparent: true,
    fog: false,
  });
  let materials = createProvinceMaterial();
  mapCurrentProperties.value = features[0].properties;
  let faceMaterial = new THREE.MeshStandardMaterial({
    color: 0xffffff,
    transparent: true,
    opacity: 0.5,
  });
  GradientShader(faceMaterial, { uColor1: 0x2a6e92, uColor2: 0x102736 });
  defaultMaterial = faceMaterial;
  defaultLightMaterial = defaultMaterial.clone();
  defaultLightMaterial.emissive.setHex(0x0b112d);
  defaultLightMaterial.emissiveIntensity = 3.5;

  features.forEach((feature) => {
    // 定一个省份3D对象
    const province = track(new THREE.Object3D());
    province.properties = feature.properties.name;
    const coordinates = feature.geometry.coordinates;
    let mesh, noodle, line;
    if (feature.geometry.type === "MultiPolygon") {
      // 多个，多边形
      coordinates.forEach((coordinate) => {
        // coordinate 多边形数据
        coordinate.forEach((rows) => {
          mesh = drawExtrudeMesh(rows, materials, projection1);
          line = lineDraw(rows, LineMaterial, projection1);
          noodle = drawMesh(rows, faceMaterial, projection1);
          // 唯一标识
          mesh.properties = feature.properties;
          noodle.properties = feature.properties;
          noodle.translateZ(plateThickness + 0.001);
          province.add(line);
          province.add(mesh);
          province.add(noodle);
          eventElement.push(noodle);
        });
      });
    }
    if (feature.geometry.type === "Polygon") {
      // 多边形
      coordinates.forEach((coordinate) => {
        mesh = drawExtrudeMesh(coordinate, materials, projection1);
        line = lineDraw(coordinate, LineMaterial, projection1);
        noodle = drawMesh(rows, faceMaterial, projection1);
        // 唯一标识
        mesh.properties = feature.properties;
        noodle.properties = feature.properties;
        noodle.translateZ(plateThickness + 0.001);
        province.add(line);
        province.add(mesh);
        province.add(noodle);
        eventElement.push(noodle);
      });
    }
    threedPlate.add(province);
    threedPlate.visible = true;
  });
  /* 添加事件 */
  eventElement.forEach((noodle) => {
    interactionManager.add(noodle);
    noodle.addEventListener("click", (ev) => {
      if (ev.target.properties && driveScoredata.value.length > 0) {
        // 点击下转 查询 adcode 对应的 市、区id 进行搜索查询
        reverseLookup(ev.target.properties.adcode);
      }
    });
    noodle.addEventListener("mouseover", (event) => {
      if (driveScoredata.value.length > 0) {
        showInfoPopupsStatus.value = true;
        if (!objectsHover.includes(event.target.parent)) {
          objectsHover.push(event.target.parent);
        }
        document.body.style.cursor = "pointer";
        move(event.target);
        showInfoPopps(event.target.properties);
      }
    });
    noodle.addEventListener("mouseout", (event) => {
      if (driveScoredata.value.length > 0) {
        reset(event.target);
        document.body.style.cursor = "default";
        showInfoPopupsStatus.value = false;
      }
    });
  });

  //   threedPlate.translateX(100);
  //   threedPlate.translateY(10);
  //   threedPlate.translateZ(100);
  scene.add(threedPlate);
  const reset = (mesh) => {
    // 还原颜色
    if (mesh) {
      mesh.traverse((obj) => {
        if (obj.isMesh) {
          obj.material = defaultMaterial;
        }
      });
    }
  };
  const move = (mesh) => {
    // 设置发光颜色
    if (mesh) {
      mesh.traverse((obj) => {
        if (obj.isMesh) {
          obj.material = defaultLightMaterial;
        }
      });
    }
  };
};
/**
 * @description: 创建精灵图标点
 */
const createPoint = (features) => {
  const projection = geoMercator()
    .center(pointCenter)
    .scale(d3Scale)
    .translate([0, 0]);
  //   let texture = new THREE.TextureLoader().load(pointPng);
  //   let spriteMaterial = track(
  //     new THREE.SpriteMaterial({
  //       map: texture,
  //       transparent: true, //使用背景透明的png贴图，注意开启透明计算
  //       opacity: 1,
  //     })
  //   );
  let option = {
    divClass: "sprite-box",
    spanClass: "sprite-box-span",
  };
  // 创建 CSS2DObject对象，参数是html元素，作用：将html元素转为threejs的css2模型对象
  if (features.length > 0) {
    features.forEach((info) => {
      if (info.data) {
        let center = projection([
          Number(info.longitude),
          Number(info.latitude),
        ]);
        const tag = new CSS2DObject(createCanvas(info.data, option));
        let guangQuan = createRing([
          center[0],
          -center[1],
          plateThickness + 0.1,
        ]);
        tag.position.set(center[0], -center[1], plateThickness + 0.2);
        threedPoint.add(tag);
        threedPoint.add(guangQuan);
      }
    });
    scene.add(threedPoint);
  }
};
/**
 * @description: 创建像素点
 */
const createPixelsPoint = (features) => {
  const projection = geoMercator()
    .center(pointCenter)
    .scale(d3Scale)
    .translate([0, 0]);

  var pointsMaterial = new THREE.PointsMaterial({
    color: 0xff0000,
    transparent: true,
    opacity: 0,
  }); //材质对象
  let option = {
    divClass: "sprite-box-1",
  };
  features.forEach((item) => {
    let center = projection([Number(item.longitude), Number(item.latitude)]);

    let sphereGeometry = new THREE.SphereGeometry(0.1, 32, 32);
    let points = new THREE.Mesh(sphereGeometry, pointsMaterial);
    let points2 = new CSS2DObject(createCanvas("", option));
    points.position.set(center[0], -center[1], plateThickness);
    points2.position.set(center[0], -center[1], plateThickness);
    points.properties = item;
    points2.properties = item;
    threedPixelsPoint.add(points2);
    threedPixelsPoint.add(points);

    interactionManager.add(points);

    points.addEventListener("mouseover", (event) => {
      if (icondata.value.length > 0) {
        showUnitInfoPopupsStatus.value = true;
        document.body.style.cursor = "pointer";
        showUnitInfoPopps(event.target.properties);
      }
    });
    points.addEventListener("mouseout", (event) => {
      document.body.style.cursor = "default";
      showUnitInfoPopupsStatus.value = false;
    });
  });
  scene.add(threedPixelsPoint);
};
/**
 * @description: 创建圆环
 * @param {*} position
 */
const createRing = (position) => {
  let geometry = new THREE.PlaneGeometry(1, 1);
  let material1 = new THREE.MeshBasicMaterial({
    color: 0xffffff,
    map: guangquan1,
    alphaMap: guangquan1,
    opacity: 1,
    transparent: true,
    depthTest: false,
    fog: false,
    blending: THREE.AdditiveBlending,
  });
  let material2 = new THREE.MeshBasicMaterial({
    color: 0xffffff,
    map: guangquan2,
    alphaMap: guangquan2,
    opacity: 1,
    transparent: true,
    depthTest: false,
    fog: false,
    blending: THREE.AdditiveBlending,
  });
  let mesh1 = new THREE.Mesh(geometry, material1);
  let mesh2 = new THREE.Mesh(geometry, material2);
  mesh1.renderOrder = 6;
  mesh2.renderOrder = 6;
  //   mesh1.rotateX(-Math.PI / 2);
  //   mesh2.rotateX(-Math.PI / 2);
  mesh1.position.set(position[0], position[1], plateThickness + 0.1);
  mesh2.position.set(position[0], position[1], plateThickness + 0.1);
  let quanGroup = new THREE.Group();
  quanGroup.add(mesh1, mesh2);
  scene.add(quanGroup);
  ringList.push(mesh1);
  //   this.time.on("tick", () => {
  //     mesh1.rotation.z += 0.05;
  //   });
  return quanGroup;
};
/**
 * @description: 创建网格·
 */
const createGrid = () => {
  let defaultOptions = {
    position: new THREE.Vector3(0, 0, 0),
    gridSize: 150,
    gridDivision: 20,
    gridColor: "#102736",
    shapeSize: 0.5,
    shapeColor: "#2a5f8a",
    pointSize: 0.1,
    pointColor: "#102736",
    pointLayout: {
      row: 400,
      col: 400,
    },
    pointBlending: THREE.NormalBlending,
    diffuse: true,
    diffuseSpeed: 10.0,
    diffuseColor: "#54b1ff",
    diffuseWidth: 50.0,
  };
  let group = new THREE.Group();
  let shapes = createShapes(defaultOptions);
  let points = createGridPoint(defaultOptions);
  group.add(shapes, points);
  group.position.set(0, 0, 0);
  group.rotateX((Math.PI / 180) * 90);
  scene.add(group);
};
// 创建+号阵列
/**
 * @description:
 */
const createShapes = (options) => {
  let { gridSize, gridDivision, shapeSize, shapeColor } = options;
  let shapeSpace = gridSize / gridDivision;
  let range = gridSize / 2;
  let shapeMaterial = new THREE.MeshBasicMaterial({
    color: shapeColor,
    side: THREE.DoubleSide,
  });
  let shapeGeometrys = [];
  for (let i = 0; i < gridDivision + 1; i++) {
    for (let j = 0; j < gridDivision + 1; j++) {
      let shapeGeometry = createPlus(shapeSize);
      shapeGeometry.translate(
        -range + i * shapeSpace,
        -range + j * shapeSpace,
        0
      );
      shapeGeometrys.push(shapeGeometry);
    }
  }

  let geometry = mergeGeometries(shapeGeometrys);
  let shapeMesh = new THREE.Mesh(geometry, shapeMaterial);
  shapeMesh.renderOrder = -1;
  shapeMesh.rotateX(-Math.PI / 2);
  shapeMesh.position.y += 0.01;
  return shapeMesh;
};
/**
 * @description: 创建网格点
 */
const createGridPoint = (options) => {
  let { gridSize, pointSize, pointColor, pointBlending, pointLayout, diffuse } =
    options;
  const rows = pointLayout.row;
  const cols = pointLayout.col;
  const positions = new Float32Array(rows * cols * 3);
  for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
      let x = (i / (rows - 1)) * gridSize - gridSize / 2;
      let y = 0;
      let z = (j / (cols - 1)) * gridSize - gridSize / 2;
      let index = (i * cols + j) * 3;
      positions[index] = x;
      positions[index + 1] = y;
      positions[index + 2] = z;
    }
  }
  var geometry = new THREE.BufferGeometry();
  geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));

  let material = new THREE.PointsMaterial({
    size: pointSize,
    sizeAttenuation: true,
    color: pointColor,
    blending: pointBlending,
  });
  const particles = new THREE.Points(geometry, material);
  if (diffuse) {
    diffuseShader(material, options);
  }
  return particles;
};
/**
 * @description:
 * @return {*}
 */
const diffuseShader = (diffmaterial, options) => {
  let { gridSize, diffuseColor, diffuseSpeed, diffuseWidth } = options;
  DiffuseShader({
    diffmaterial,
    size: gridSize,
    diffuseColor,
    diffuseSpeed,
    diffuseWidth,
  });
  return false;
};
const createPlus = (shapeSize = 50) => {
  let w = shapeSize / 6 / 3;
  let h = shapeSize / 3;
  let points = [
    new THREE.Vector2(-h, -w),
    new THREE.Vector2(-w, -w),
    new THREE.Vector2(-w, -h),
    new THREE.Vector2(w, -h),
    new THREE.Vector2(w, -h),
    new THREE.Vector2(w, -w),
    new THREE.Vector2(h, -w),
    new THREE.Vector2(h, w),
    new THREE.Vector2(w, w),
    new THREE.Vector2(w, h),
    new THREE.Vector2(-w, h),
    new THREE.Vector2(-w, w),
    new THREE.Vector2(-h, w),
  ];
  let shape = new THREE.Shape(points);
  let shapeGeometry = new THREE.ShapeGeometry(shape, 24);
  return shapeGeometry;
};
/**
 * 立体几何图形
 * @param polygon 多边形 点数组
 * @param color 材质颜色
 * */
const drawExtrudeMesh = (polygon, materials, projection) => {
  const shape = new THREE.Shape();
  polygon.forEach((row, i) => {
    const [x, y] = projection(row);
    if (i === 0) {
      shape.moveTo(x, -y);
    }
    shape.lineTo(x, -y);
  });

  // 拉伸
  const geometry = new THREE.ExtrudeGeometry(shape, {
    depth: plateThickness,
    bevelEnabled: false,
  });
  //   const material = new THREE.MeshBasicMaterial({
  //     color: color,
  //     transparent: true,
  //     opacity: 0.5,
  //   });
  return new THREE.Mesh(geometry, materials);
};
/**
 *  面绘制
 * @param polygon 多边形 点数组
 * @param color 材质颜色
 * */
const drawMesh = (polygon, material, projection) => {
  const shape = new THREE.Shape();
  polygon.forEach((row, i) => {
    const [x, y] = projection(row);
    if (i === 0) {
      shape.moveTo(x, -y);
    }
    shape.lineTo(x, -y);
  });

  const geometry = new THREE.ShapeGeometry(shape);
  return new THREE.Mesh(geometry, material);
};
/**
 * 边框 图形绘制
 * @param polygon 多边形 点数组
 * @param color 材质颜色
 * */
const lineDraw = (polygon, LineMaterial, projection) => {
  const lineGeometry = new THREE.BufferGeometry();
  const pointsArray = new Array();
  polygon.forEach((row) => {
    const [x, y] = projection(row);
    // 创建三维点
    pointsArray.push(new THREE.Vector3(x, -y, plateThickness));
  });
  // 放入多个点
  lineGeometry.setFromPoints(pointsArray);
  return new THREE.Line(lineGeometry, LineMaterial);
};
/**
 * @description: 生成一个canvas对象
 * @param {*} name
 * @param {*} option
 * @return {*}
 */
const createCanvas = (name, option = { divClass: "sprite-box" }) => {
  // 创建div元素
  let divElement = document.createElement("div");
  divElement.className = option.divClass;
  if (option.spanClass) {
    // 创建span元素
    let spanElement = document.createElement("span");
    spanElement.innerHTML = name;
    spanElement.className = option.spanClass;
    // 将span元素添加到div中
    divElement.appendChild(spanElement);
  }
  return divElement;
};
/**
 * @description: 修改着色器
 * @param {*} material
 * @param {*} option
 */
const GradientShader = (material, option) => {
  let config = Object.assign(
    {
      uColor1: "#2a6f72",
      uColor2: "#0d2025",
      size: 15.0,
      dir: "x",
    },
    option
  );
  let { uColor1, uColor2, dir, size } = config;
  let dirMap = { x: 1.0, y: 2.0, z: 3.0 };
  material.onBeforeCompile = (shader) => {
    shader.uniforms = {
      ...shader.uniforms,
      uColor1: { value: new THREE.Color(uColor1) },
      uColor2: { value: new THREE.Color(uColor2) },
      uDir: { value: dirMap[dir] },
      uSize: { value: size },
    };
    shader.vertexShader = shader.vertexShader.replace(
      "void main() {",
      `
                attribute float alpha;
                varying vec3 vPosition;
                varying float vAlpha;
                void main() {
                  vAlpha = alpha;
                  vPosition = position;
              `
    );
    shader.fragmentShader = shader.fragmentShader.replace(
      "void main() {",
      `
                varying vec3 vPosition;
                varying float vAlpha;
                uniform vec3 uColor1;
                uniform vec3 uColor2;
                uniform float uDir;
                uniform float uSize;

                void main() {
              `
    );
    shader.fragmentShader = shader.fragmentShader.replace(
      "#include <opaque_fragment>",
      /* glsl */ `
              #ifdef OPAQUE
              diffuseColor.a = 1.0;
              #endif

              // https://github.com/mrdoob/three.js/pull/22425
              #ifdef USE_TRANSMISSION
              diffuseColor.a *= transmissionAlpha + 0.1;
              #endif
              // vec3 gradient = mix(uColor1, uColor2, vPosition.x / 15.0);
              vec3 gradient = vec3(0.0,0.0,0.0);
              if(uDir==1.0){
                gradient = mix(uColor1, uColor2, vPosition.x/ uSize);
              }else if(uDir==2.0){
                gradient = mix(uColor1, uColor2, vPosition.z/ uSize);
              }else if(uDir==3.0){
                gradient = mix(uColor1, uColor2, vPosition.y/ uSize);
              }
              outgoingLight = outgoingLight*gradient;


              gl_FragColor = vec4( outgoingLight, diffuseColor.a  );
              `
    );
  };
};
let gradPointShader = null;
let gradPointMaxTime = null;
let deltaTime = new THREE.Clock();
const DiffuseShader = (option) => {
  let defaultOptions = {
    size: 100,
    diffuseSpeed: 10.0,
    diffuseColor: "#60e5ff",
    diffuseWidth: 200.0,
  };
  let options = Object.assign({}, defaultOptions, option);

  let { diffmaterial, size, diffuseColor, diffuseSpeed, diffuseWidth } =
    options;
  gradPointMaxTime = size / diffuseSpeed;
  diffmaterial.onBeforeCompile = (shader) => {
    gradPointShader = shader;
    shader.uniforms = {
      ...shader.uniforms,
      uTime: {
        value: 0.0,
      },
      uSpeed: {
        value: diffuseSpeed,
      },
      uWidth: {
        value: diffuseWidth,
      },
      uColor: {
        value: new THREE.Color(diffuseColor),
      },
    };
    shader.vertexShader = shader.vertexShader.replace(
      "void main() {",
      /* glsl */ `
            varying vec3 vPosition;
            void main(){
              vPosition = position;
          `
    );
    shader.fragmentShader = shader.fragmentShader.replace(
      "void main() {",
      /* glsl */ `
            uniform float uTime;
            uniform float uSpeed;
            uniform float uWidth;
            uniform vec3 uColor;
            varying vec3 vPosition;
            void main(){
          `
    );
    shader.fragmentShader = shader.fragmentShader.replace(
      "#include <opaque_fragment>",
      /* glsl */ `
            #ifdef OPAQUE
            diffuseColor.a = 1.0;
            #endif

            #ifdef USE_TRANSMISSION
            diffuseColor.a *= material.transmissionAlpha;
            #endif

            float r = uTime * uSpeed;

            float w = 0.0;
            if(w>uWidth){
              w = uWidth;
            }else{
              w = uTime * 5.0;
            }

            vec2 center = vec2(0.0, 0.0);

            float rDistance = distance(vPosition.xz, center);

            if(rDistance > r && rDistance < r + 2.0 * w) {
              float per = 0.0;
              if(rDistance < r + w) {
                per = (rDistance - r) / w;
                outgoingLight = mix(outgoingLight, uColor, per);
              } else {
                per = (rDistance - r - w) / w;
                outgoingLight = mix(uColor, outgoingLight, per);
              }
              gl_FragColor = vec4(outgoingLight, diffuseColor.a);
            } else {
              gl_FragColor = vec4(outgoingLight, 0.0);
            }
          `
    );
  };

  //   this.time.on("tick", (deltaTime) => {
  //     if (pointShader) {
  //       pointShader.uniforms.uTime.value += deltaTime;
  //       if (pointShader.uniforms.uTime.value > maxTime) {
  //         pointShader.uniforms.uTime.value = 0;
  //       }
  //     }
  //   });
};
/**
 * @description: 清除threeJs 物体对象
 * @param {*} object
 */
const disposeAndRemoveChildren = (object) => {
  for (var i = object.children.length - 1; i >= 0; i--) {
    var child = object.children[i];

    // 如果子物体有geometry或material，先释放这些资源
    if (child.geometry) {
      child.geometry.dispose();
    }
    if (child.material) {
      if (Array.isArray(child.material)) {
        child.material.forEach((material) => material.dispose());
      } else {
        child.material.dispose();
      }
    }

    // 移除子物体
    object.remove(child);

    // 递归处理孙子级子物体
    disposeAndRemoveChildren(child);
  }
};
defineExpose({ accept });
</script>

<style scoped lang="scss">
.map-gd {
  width: 100vw;
  height: 100vh;
  position: relative;
  #province {
    width: 100vw;
    height: 100vh;
  }
  .back-btn {
    cursor: pointer;
    position: absolute;
    bottom: 28%;
    left: 25%;
    width: 80px;
    height: 30px;
    background: url("@/assets/images/mapback.png") no-repeat;
  }
  .info-popups {
    position: absolute;
    padding: 5px 13px;
    border-radius: 4px;
    background: linear-gradient(
      180deg,
      rgba(4, 27, 36, 0.7),
      rgba(14, 59, 94, 0.7)
    );
    background-blend-mode: multiply;
    border: 1px solid #24757b;
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    z-index: 100;
    font-size: 14px;
    .popups-li {
      width: 100%;
      padding: 5px 0;
      display: flex;
      flex-direction: row;
      flex-wrap: nowrap;
      align-items: center;
      span {
        color: #fff;
      }
      .span-green {
        color: green;
      }
      .span-orange {
        color: orange;
      }
      .span-red {
        color: red;
      }
    }
  }
  .street-info-popups {
    width: 245px;
    padding: 5px 15px;
    font-size: 12px;
    .popups-li {
      padding: 2px 0;
      align-items: flex-start;
      justify-content: space-between;
      .li-label {
        width: 40%;
      }
      .li-info {
        width: 55%;
      }
    }
  }
  .full-right {
    width: 19%;
    height: 35vh;
    position: absolute;
    top: 39%;
    right: 25%;
    overflow-y: scroll;
    z-index: 15;
    .right-item {
      color: #ffffff;
      font-size: 12px;
      padding: 6px 0px 6px 10px;
      display: flex;
      justify-content: space-evenly;
      flex-direction: row;
      flex-wrap: nowrap;
      align-items: center;
      .num {
        width: 20%;
        color: #51a6dd;
      }
      .city {
        width: 35%;
      }
      .proportion {
        width: 15%;
        text-align: right;
      }
    }
    .right-item:nth-child(odd) {
      background-color: rgba(24, 49, 71, 0.8);
    }
    .right-item:nth-child(even) {
      background-color: rgba(15, 32, 50, 0.4);
    }
  }
  .full-right::-webkit-scrollbar {
    display: none;
  }
  //   .full-top {
  //     position: absolute;
  //     top: 12%;
  //     left: 50%;
  //     transform: translate(-50%, 0);
  //     width: 50%;
  //     height: 130px;
  //     padding: 5px 20px;
  //   }
  .full-top {
    position: absolute;
    top: 17%;
    left: 64.5%;
    transform: translate(-50%, 0);
    width: 21%;
    display: flex;
    justify-content: space-between;
    flex-direction: row;
    flex-wrap: wrap;
    z-index: 15;
    .top-item {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      width: 200px;
      height: 90px;

      background: url("@/assets/images/mapback1.png") no-repeat;
      background-size: 100% 100%;
      color: #ffffff;
      font-size: 12px;
      padding-left: 40px;
      .item-title {
        font-size: 14px;
      }
      .item-num {
        font-weight: bold;
        font-size: 20px;
        .number {
          color: #ffffff;
          background: linear-gradient(180deg, #83d0fe 0%, #ffffff 100%);
          -webkit-background-clip: text;
          -webkit-text-fill-color: transparent;
        }
        .unit {
          font-size: 12px;
          color: #a0a5ab !important;
        }
      }
    }
    .special-item {
      cursor: pointer;
    }
    .top-item:nth-child(2) {
      background: url("@/assets/images/mapback2.png") no-repeat;
      background-size: 100% 100%;
      .item-num {
        .number {
          color: #ffffff;
          background: linear-gradient(180deg, #38b7d2 0%, #ffffff 100%);
          -webkit-background-clip: text;
          -webkit-text-fill-color: transparent;
        }
      }
    }
    .top-item:nth-child(3) {
      background: url("@/assets/images/mapback3.png") no-repeat;
      background-size: 100% 100%;
      .item-num {
        .number {
          color: #ffffff;
          background: linear-gradient(180deg, #f94315 0%, #ffffff 100%);
          -webkit-background-clip: text;
          -webkit-text-fill-color: transparent;
        }
      }
    }
    .top-item:nth-child(4) {
      background: url("@/assets/images/mapback4.png") no-repeat;
      background-size: 100% 100%;
      .item-num {
        .number {
          color: #ffffff;
          background: linear-gradient(180deg, #eaac3a 0%, #ffffff 100%);
          -webkit-background-clip: text;
          -webkit-text-fill-color: transparent;
        }
      }
    }
  }
  .full-top-vertical {
    display: flex;
    justify-content: space-between;
    flex-direction: column;
    flex-wrap: wrap;
    width: 200px;
    top: 25%;
    left: 69.8%;
  }
  .full-bottom {
    cursor: pointer;
    position: absolute;
    top: 13%;
    left: 50%;
    transform: translate(-50%, 0);
    width: 50%;
    padding: 5px 20px;
    height: 30px;
    background: url("@/assets/images/danderbac.png") no-repeat;
    background-size: 100% 100%;
    z-index: 15;
    .bottom-item {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 0 12px;
      font-size: 12px;
      color: #ffffff;
      height: 30px;
      transform: translate(0px, -5px);
      .title {
        font-size: 15px;
        font-weight: 700;
        background: linear-gradient(180deg, #d09696 0%, #ffffff 100%);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
      }
    }
  }
  .scroll {
    height: 100%;
    width: 100%;
    overflow: hidden;
  }
}
</style>
<style lang="scss">
.sprite-box {
  background: url("@/assets/images/img-chart-map-annotation-1.png") no-repeat;
  width: 22px;
  height: 28px;
  background-size: 100% 100%;
  .sprite-box-span {
    width: 15px;
    height: 15px;
    border-radius: 50%;
    margin: 0 auto;
    margin-top: -8px;
    background: #ff3c3c;
    font-size: 10px;
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: center;
    border: 1px solid #ff8585;
    color: #fff;
  }
}
.sprite-box-1 {
  background: url("@/assets/images/img-chart-map-annotation-2.png") no-repeat;
  width: 22px;
  height: 28px;
  background-size: 100% 100%;
}
</style>
